#region Copyright Notice
// 
// 
//  
//  Copyright (c) 2009-2011 Anindya Chatterjee
//  
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//  
//      http://www.apache.org/licenses/LICENSE-2.0
//  
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
// 
// 
#endregion
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization.Json;
using AbstractClass.Runtime.Serialization;

namespace AbstractClass.Extensions.Serialization
{
    /// <summary>
    /// Provides a set of extension methods for <see cref="object"/>.
    /// </summary>
    public static class ObjectExtensions
    {
        /// <summary>
        ///   Converts the <paramref name="obj"/> to its xml representation.
        /// </summary>
        /// <typeparam name = "T">Type of the object.</typeparam>
        /// <param name = "obj">The object to xml serialize.</param>
        /// <returns>A xml string representing the <paramref name="obj"/>.</returns>
        /// <exception cref="SerializationException">Serialization failed.</exception>
        /// <remarks><paramref name="obj"/> has to be marked as <see cref="SerializableAttribute"/>.</remarks>
        public static string ToXmlString<T>(this T obj) where T : class
        {
            try
            {
                using (var ms = new MemoryStream())
                {
                    var ser = new NetDataContractSerializer();
                    ser.Serialize(ms, obj);
                    ms.Flush();

                    ms.Position = 0;
                    var streamReader = new StreamReader(ms);
                    string text = streamReader.ReadToEnd();
                    return text;
                }
            }
            catch (Exception ex)
            {
                throw new SerializationException(LanguageResource.ObjectExtensionsToXmlStringFailedToSerializeObject, ex);
            }
        }

        /// <summary>
        ///   Recreates an object from its xml representation.
        /// </summary>
        /// <typeparam name="T">Type of the object.</typeparam>
        /// <param name="obj">The object.</param>
        /// <param name="xml">The xml string to deserialize.</param>
        /// <returns>An <see cref="Object"/> of type <typeparamref name="T"/>.</returns>
        /// <exception cref="SerializationException">Deserialization failed.</exception>
        /// <remarks><paramref name="obj"/> has to be marked as <see cref="SerializableAttribute"/>.</remarks>
        public static T FromXmlString<T>(this T obj, string xml) where T : class
        {
            try
            {
                using (var ms = new MemoryStream())
                {
                    var sw = new StreamWriter(ms);
                    sw.Write(xml);
                    sw.Flush();
                    ms.Position = 0;

                    var ser = new NetDataContractSerializer();
                    var newObj = (T) ser.Deserialize(ms);

                    return newObj;
                }
            }
            catch (Exception ex)
            {
                throw new SerializationException(LanguageResource.ObjectExtensionsFromXmlStringFailedToDeserialize, ex);
            }
        }


        /// <summary>
        ///   Converts the <paramref name="obj"/> to its json representation.
        /// </summary>
        /// <typeparam name = "T">Type of the object.</typeparam>
        /// <param name = "obj">The object to json serialize.</param>
        /// <returns>A json string representing the <paramref name="obj"/>.</returns>
        /// <exception cref="SerializationException">Serialization failed.</exception>
        public static string ToJsonString<T>(this T obj) where T : class
        {
            try
            {
                using (var ms = new MemoryStream())
                {
                    var serializer = new DataContractJsonSerializer(typeof (T));
                    serializer.WriteObject(ms, obj);
                    ms.Flush();

                    ms.Position = 0;
                    var streamReader = new StreamReader(ms);
                    string text = streamReader.ReadToEnd();
                    return text;
                }
            }
            catch (Exception ex)
            {
                throw new SerializationException(LanguageResource.ObjectExtensionsToXmlStringFailedToSerializeObject, ex);
            }
        }


        /// <summary>
        ///   Recreates an object from its json representation.
        /// </summary>
        /// <typeparam name="T">Type of the object.</typeparam>
        /// <param name="obj">The object.</param>
        /// <param name="json">The json string to deserialize.</param>
        /// <returns>An <see cref="Object"/> of type <typeparamref name="T"/>.</returns>
        /// <exception cref="SerializationException">Deserialization failed.</exception>
        public static T FromJsonString<T>(this T obj, string json) where T : class
        {
            try
            {
                using (var ms = new MemoryStream())
                {
                    var sw = new StreamWriter(ms);
                    sw.Write(json);
                    sw.Flush();
                    ms.Position = 0;

                    var ser = new DataContractJsonSerializer(typeof (T));
                    var newObj = ser.ReadObject(ms) as T;

                    return newObj;
                }
            }
            catch (Exception ex)
            {
                throw new SerializationException(LanguageResource.ObjectExtensionsFromXmlStringFailedToDeserialize, ex);
            }
        }


        /// <summary>
        ///   Converts the <paramref name = "obj" /> to its binary <see cref ="Stream"/> representation.
        /// </summary>
        /// <typeparam name = "T">Type of the object.</typeparam>
        /// <param name = "obj">The object to binary serialize.</param>
        /// <returns>A binary stream representing the <paramref name="obj"/>.</returns>
        /// <exception cref = "SerializationException">Serialization failed.</exception>
        public static Stream ToBinaryStream<T>(this T obj) where T : class
        {
            Stream stream = null;
            try
            {
                var formatter = new BinaryFormatter {SurrogateSelector = new SurrogateSelector()};
                formatter.SurrogateSelector.ChainSelector(
                    new NonSerialiazableTypeSurrogateSelector());
                stream = new MemoryStream();
                formatter.Serialize(stream, obj);
                stream.Position = 0;
                return stream;
            }
            catch (Exception ex)
            {
                if (stream != null)
                    stream.Dispose();
                throw new SerializationException(LanguageResource.ObjectExtensionsToXmlStringFailedToSerializeObject, ex);
            }
        }

        /// <summary>
        ///   Recreates an object from its binary representation.
        /// </summary>
        /// <typeparam name = "T">Type of the object.</typeparam>
        /// <param name = "obj">The object.</param>
        /// <param name = "binaryStream">The binary stream to deserialize.</param>
        /// <returns>An <see cref="Object"/> of type <typeparamref name="T"/>.</returns>
        /// <exception cref = "SerializationException">Deserialization failed.</exception>
        public static T FromBinaryStream<T>(this T obj, Stream binaryStream) where T : class
        {
            try
            {
                var formatter = new BinaryFormatter {SurrogateSelector = new SurrogateSelector()};
                formatter.SurrogateSelector.ChainSelector(
                    new NonSerialiazableTypeSurrogateSelector());
                binaryStream.Position = 0;
                return (T) formatter.Deserialize(binaryStream);
            }
            catch (Exception ex)
            {
                throw new SerializationException(LanguageResource.ObjectExtensionsFromXmlStringFailedToDeserialize, ex);
            }
        }
    }
}